package xcmg.device.web.minio.config;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.minio.BucketExistsArgs;
import io.minio.GetObjectArgs;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveBucketArgs;
import io.minio.SetBucketPolicyArgs;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import yb.ecp.fast.infra.util.SnowflakeIdWorker;

import javax.annotation.PostConstruct;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 官方文档：https://docs.min.io/docs/java-client-api-reference.html
 */
@Slf4j
@Component
public class MinioUtil {

    @Autowired
    private MinioProp minioProp;

    private final SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 0);

    private CustomMinioClient client;

    @PostConstruct
    public void init() {
        MinioClient minioClient = MinioClient.builder()
                .endpoint(minioProp.getEndpoint())
                .credentials(minioProp.getAccessKey(), minioProp.getSecretKey())
                .build();
        client = new CustomMinioClient(minioClient);
    }

    /**
     * 占位符
     */
    private static final String BUCKET_PARAM = "${bucket}";

    /**
     * bucket权限-读写
     */
    private static final String READ_WRITE = "{\"Version\":\"2023-01-04\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";


    /**
     * bucket权限-只读
     */
    private static final String WRITE_ONLY = "{\"Version\":\"2023-01-04\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";


    /**
     * 创建bucket
     */
    public void createBucket(String bucketName) throws Exception {
        if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
            client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    public void setBucketPolicy(String bucketName) throws Exception {
        String policy = json2String("policyJson.json", bucketName);
        client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(READ_WRITE.replace(BUCKET_PARAM, bucketName)).build());
    }

    /**
     * 需要在resource下先创建好文件
     * json转String
     *
     * @param path       文件路径
     * @param bucketName 这里是传入的桶名称，执行完后会替换掉原json文件中的MybuckerName
     * @return
     * @throws IOException
     */
    public static String json2String(String path, String bucketName) throws IOException {
        StringBuilder result = new StringBuilder();
        try (InputStream in = new ClassPathResource(path).getInputStream(); InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8); BufferedReader bufr = new BufferedReader(isr)) {
            String line;
            while ((line = bufr.readLine()) != null) {
                result.append(System.lineSeparator()).append(line);
            }
        }
        JSONObject parse = (JSONObject) JSONObject.parse(result.toString());
        JSONArray statement = (JSONArray) parse.get("Statement");
        JSONObject o = (JSONObject) statement.get(0);
        o.remove("Resource");
        o.fluentPut("Resource", "arn:aws:s3:::" + bucketName);
        JSONObject o1 = (JSONObject) statement.get(1);
        o1.remove("Resource");
        o1.fluentPut("Resource", "arn:aws:s3:::" + bucketName + "/*");
        return parse.toString();
    }

    /**
     * 获取全部bucket信息
     *
     * @return
     */
    public List<Bucket> getAllBuckets() throws Exception {
        return client.listBuckets();
    }

    /**
     * 根据bucketName删除信息
     *
     * @param bucketName bucket名称
     */
    public void removeBucket(String bucketName) throws Exception {
        client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 上传文件
     *
     * @param file
     * @param bucketName
     * @return
     */
    public Map<String, Object> uploadFile(MultipartFile file, String bucketName) throws Exception {
        if (null == file || 0 == file.getSize()) {
            return null;
        }
        createBucket(bucketName);
        String originalFilename = file.getOriginalFilename();
        assert originalFilename != null;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String fileName = "U_" + format.format(new Date()) + "_" + idWorker.nextId() + originalFilename.substring(originalFilename.lastIndexOf("."));
        client.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(
                                file.getInputStream(), file.getSize(), -1)
                        .contentType(file.getContentType())
                        .build());
        String url = "/" + bucketName + "/" + fileName;
        String urlHost = minioProp.getFilHost() + "/" + bucketName + "/" + fileName;
        log.info("上传文件成功url ：[{}], urlHost ：[{}]", url, urlHost);
        return fileUploadRspMap(url, urlHost, fileName);
    }

    public Map<String, Object> uploadImgFile(MultipartFile file, String bucketName, String qualityStr) throws Exception {
        if (null == file || 0 == file.getSize()) {
            return null;
        }
        String originalFilename = file.getOriginalFilename();
        assert originalFilename != null;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String fileName = "U_" + format.format(new Date()) + "_" + idWorker.nextId() + originalFilename.substring(originalFilename.lastIndexOf("."));
        float quality = Float.parseFloat((new BigDecimal(qualityStr)).setScale(2, RoundingMode.DOWN).toString());
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        Thumbnails.of(file.getInputStream())
                .scale(1f)
                .outputQuality(quality)
                .useOriginalFormat().toOutputStream(outputStream);
        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());

        client.putObject(PutObjectArgs.builder()
                .bucket(bucketName)
                .object(fileName)
                .stream(inputStream, outputStream.size(), -1)
                .contentType(file.getContentType())
                .build());
        String url = "/" + bucketName + "/" + fileName;
        String urlHost = minioProp.getFilHost() + "/" + bucketName + "/" + fileName;
        log.info("上传文件成功url ：[{}], urlHost ：[{}]", url, urlHost);
        return fileUploadRspMap(url, urlHost, fileName);
    }

    private Map<String, Object> fileUploadRspMap(String url, String urlHost, String fileName) {
        Map<String, Object> r = new HashMap<>();
        r.put("url", url);
        r.put("urlHost", urlHost);
        r.put("fileName", fileName);
        return r;
    }

    /**
     * 获取文件外链
     *
     * @param bucketName
     * @param region
     * @param fileName
     * @return
     */
    public String getObjectUrl(String bucketName, String region, String fileName) throws Exception {
        return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).region(region).object(fileName).build());
    }


    public void getObject(String bucketName, String objectName, HttpServletResponse response) throws Exception {
        try (InputStream stream = client.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build())) {
            response.reset();
            response.setContentType("application/octet-stream");
            response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(objectName, String.valueOf(StandardCharsets.UTF_8)));
            ServletOutputStream outputStream = response.getOutputStream();
            byte[] b = new byte[8192]; // 增大缓冲区大小
            int len;
            while ((len = stream.read(b)) > 0) {
                outputStream.write(b, 0, len);
            }
        }
    }
}
